/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package E_explicito_EstaticoDCWC;

import java.util.ArrayList;

/**
 *
 * @author rafaelob
 */
public class TabelaHash {

    private int Rpoint;
    private int tamanhoTabela;
    private Celula celulas[];

    public TabelaHash(int tamanho) {
        celulas = new Celula[tamanho];
        this.tamanhoTabela = tamanho;
        for (int i = 0; i < tamanho; i++) { //Preenche a tabela com as células de acordo com o tamanho da tabela hash
            celulas[i] = new Celula(i);
        }
        Rpoint = tamanhoTabela - 1;//O ponteiro será posicionado na última posição da tabela.
    }

    private int getIndex(int chave) { //calcula a função hash associada a essa chave
        return chave % tamanhoTabela;
    }

    public Celula getCelula(int posicao) {
        return celulas[posicao];
    }

    private void atualizaRpoint() {
        do {
            if (celulas[Rpoint].getRecord() == null)//Se a posição que o Rpoint aponta está vazia, então não precisamos atualizá-lo
            {
                break;
            }
            this.Rpoint--; //Decrementamos até achar uma posição vazia, ou a Tabela está cheia se Rpoint for < 0
        } while (this.Rpoint >= 0);
    }

    private void atualizaPointAfterPosition(Celula celula) { //Atualiza o ponteiro que apontava para a Celula que foi passado como parâmetro para Null
        int index = getIndex(celula.getRecord().getChave());
        Celula auxiliar = celulas[index]; //Variável auxiliar para percorrer al ista
        while ((auxiliar.getPoint() != null)) {
            if (auxiliar.getPoint() == celula) {
                auxiliar.setPoint(null); //atualiza o ponteiro para Null
                break;
            } else {
                auxiliar = auxiliar.getPoint();

            }

        }

    }

    public int insertion(int chave) {
        //Atualiza o Rpoint até achar uma posição vazia

        Record record = new Record(chave);
        int index = getIndex(chave);
        if (celulas[index].insertRecord(record)) {
            return 0; // inserido sem colisão
        } else { //A colisão ocorreu, vamos procurar uma posição livre através do Rpoint
            atualizaRpoint();
            if (Rpoint < 0) {
                return -1; //Tabela Está Cheia, não vamos inserir
            } else {
                if (celulas[index].getRecord().getChave() % tamanhoTabela == index) { // O registro está no local certo dele, então vamos inserí-lo na posição do Rpoint
                    Celula atual = celulas[index]; //Variável auxiliar para percorrer a lista
                    Celula proximo = atual.getPoint(); //Variável auxiliar
                    Celula temp; //Variável Auxiliar
                    while (proximo != null) {
                        temp = proximo;
                        proximo = atual.getPoint();
                        atual = temp;
                    }
                    celulas[Rpoint].insertRecord(record); //Insere o registro no local vazio
                    atual.setPoint(celulas[Rpoint]); // Atualiza o ponteiro do posição anterior dessa lista
                    Rpoint--; //Decrementando o Rpoint
                    return 1; //inserido com Colisão


                } else { //Se o registro dessa posição não está na posição dele, então teremos que rearrumar
                    ArrayList<Record> registros = new ArrayList<Record>(); //Lista de todos os registros que serão re-inseridos
                    Celula atual, proximo; //Variáveis auxiliares utilizadas para percorrer a lista do registro
                    registros.add(celulas[index].getRecord());
                    atual = celulas[index].getPoint();
                    //Insere o registro na posição Home dele
                    atualizaPointAfterPosition(celulas[index]);//Seta o ponteiro que apontava para esse nó que vai ser re-inserido para null
                    celulas[index].setRecord(record);
                    celulas[index].setPoint(null); //Seta o ponteiro para Null

                    while (atual != null) { //Percorrer a Lista dos registros que precisam ser re-inseridos
                        registros.add(atual.getRecord());
                        proximo = atual.getPoint();
                        atual.setPoint(null);//Setando o valor do ponteiro dele para Null
                        atual.setRecord(null);//Setando o registro dele para null
                        atual = proximo;

                    }
                    Rpoint = tamanhoTabela - 1; //Pois como vamos remover os registros para reinseri-los, teremos posições vazias, então o Rpoint tem que ser setado com o valor do final da tabela
                    for (int x = 0; x < registros.size(); x++) { //Reinserindo os registros
                        insertion(registros.get(x).getChave());
                    }

                }
                return 2; //Inserido com Colisão e com necessidade de reorganizar
            }

        }

    }

    public int search(int chave) {
        int index = getIndex(chave);
        int acessos = 1;
        Celula auxiliar = celulas[index]; //Variável auxiliar para percorrer al ista

        while ((!auxiliar.searchRecord(chave)) && (auxiliar.getPoint() != null)) {
            auxiliar = auxiliar.getPoint();
            acessos++;
        }
        return acessos;


    }
}


